PyTest Basics
PyTest is a popular Python testing framework used to write, organize, execute, and maintain automated tests. It provides a simple syntax for creating test cases and includes powerful features such as fixtures, assertions, parameterization, markers, test discovery, plugins, and detailed test reporting.
PyTest is widely used for unit testing, integration testing, API testing, web automation, and Selenium-based browser automation. Its simple structure allows testers and developers to create readable and maintainable automated test suites with less boilerplate code.
In Selenium automation, PyTest can be combined with Selenium WebDriver to automate browsers, validate web application functionality, execute multiple test scenarios, manage browser setup and cleanup, and generate test execution reports.
Course Resource: Selenium Training | Register for Course Demo
1. What is PyTest?
PyTest is an open-source testing framework for Python. It is designed to make writing and running tests simple while also supporting advanced testing requirements.
PyTest can be used to test individual functions, classes, modules, APIs, databases, web applications, and complete application workflows.
One of the major advantages of PyTest is that a basic test can be written using a normal Python function and a simple assert statement.
def test_addition():
result = 10 + 20
assert result == 30
If the assertion is true, the test passes. If the assertion is false, PyTest reports the test as failed.
2. Why is PyTest Important?
PyTest is important because it provides a flexible and scalable way to automate software testing using Python.
- Simple and readable test syntax.
- Powerful assertion support.
- Automatic test discovery.
- Fixtures for setup and cleanup.
- Parameterized testing.
- Test grouping using markers.
- Detailed failure information.
- Large plugin ecosystem.
- Easy integration with Selenium.
- Easy integration with API testing tools.
- Supports parallel test execution through plugins.
- Can generate different types of test reports.
- Works well with CI/CD systems.
3. PyTest Installation
PyTest can be installed using Python's package manager, pip.
pip install pytest
After installation, verify the PyTest version:
pytest --version
A successful installation displays the installed PyTest version and related information.
4. Checking Python Installation
Before installing PyTest, Python should be installed on the system.
python --version
On some systems, the following command may be required:
python3 --version
The Python version can also be checked from an IDE terminal such as PyCharm or Visual Studio Code.
5. Creating a Basic PyTest Test
A PyTest test can be created using a Python function whose name starts with test_.
def test_login():
username = "admin"
password = "admin123"
assert username == "admin"
assert password == "admin123"
The function can then be executed by PyTest.
6. PyTest Test Naming Convention
PyTest uses naming conventions to automatically discover tests.
Common test file naming patterns include:
test_login.py
test_search.py
test_checkout.py
Test functions commonly follow this pattern:
def test_login():
pass
def test_search():
pass
def test_checkout():
pass
Following the standard naming convention makes automatic test discovery easier.
7. PyTest Test Discovery
Test discovery is the process by which PyTest automatically identifies test files, test functions, and test classes.
By default, PyTest commonly discovers files beginning with test_ or ending with _test.py.
For example:
project/
|-- test_login.py
|-- test_search.py
|-- test_checkout.py
Running:
pytest
allows PyTest to search the current directory and execute discovered tests.
8. Running PyTest Tests
The basic command for executing PyTest tests is:
pytest
To run a specific test file:
pytest test_login.py
To run a specific test function:
pytest test_login.py::test_login
To display more detailed output:
pytest -v
The -v option means verbose mode.
9. Basic PyTest Execution Flow
PyTest Command
|
v
Test Discovery
|
v
Collect Test Files
|
v
Collect Test Functions
|
v
Execute Tests
|
v
Assertions
|
v
Pass / Fail / Skip
|
v
Test Result
|
v
Report
10. Assertions in PyTest
Assertions are used to verify whether the actual result matches the expected result.
def test_addition():
result = 5 + 5
assert result == 10
If the condition is true, the test passes.
If the condition is false:
def test_addition():
result = 5 + 5
assert result == 20
PyTest marks the test as failed and provides information about the assertion failure.
11. Common Assertion Examples
def test_assertions():
assert 10 == 10
assert "Python" == "Python"
assert 10 > 5
assert 5 < 10
assert "PyTest" in "PyTest Automation"
assert True
Assertions can be used to validate values, strings, collections, Boolean conditions, and application results.
12. Assertion Failure Example
def test_failure():
expected = "Login Successful"
actual = "Login Failed"
assert actual == expected
The test fails because the actual value does not match the expected value.
13. PyTest Test Class
Multiple related test methods can be organized inside a class.
class TestLogin:
def test_valid_login(self):
assert True
def test_invalid_login(self):
assert True
def test_logout(self):
assert True
PyTest can discover test classes whose names follow its test-class naming conventions, such as classes beginning with Test.
14. Test Class Example
class TestCalculator:
def test_addition(self):
assert 10 + 20 == 30
def test_subtraction(self):
assert 20 - 10 == 10
def test_multiplication(self):
assert 5 * 5 == 25
def test_division(self):
assert 20 / 5 == 4
Grouping related tests into classes can improve test organization.
15. PyTest Fixtures
Fixtures are one of the most important features of PyTest. They provide reusable setup and preparation logic for tests.
Fixtures can be used for:
- Browser initialization.
- Database connections.
- API clients.
- Test data preparation.
- Login setup.
- File preparation.
- Environment configuration.
- Cleanup activities.
import pytest
@pytest.fixture
def sample_data():
return 10
def test_value(sample_data):
assert sample_data == 10
16. Fixture Execution Flow
Test Starts
|
v
Fixture Executes
|
v
Fixture Provides Data / Resource
|
v
Test Method Executes
|
v
Assertion
|
v
Test Completes
|
v
Fixture Cleanup
17. Fixture with Selenium
Fixtures are commonly used to create and close Selenium WebDriver instances.
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
driver.quit()
def test_google_title(driver):
driver.get("https://www.google.com")
assert "Google" in driver.title
Here, the fixture creates the browser before the test and closes the browser after the test.
18. Understanding yield in Fixtures
The yield statement is commonly used in PyTest fixtures to separate setup and teardown logic.
@pytest.fixture
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()
The code before yield runs during setup. The code after yield runs during cleanup.
19. Fixture Scope
PyTest fixtures support different scopes that control how frequently the fixture is created.
| Scope | Meaning |
| function | Runs for each test function. |
| class | Runs once for each test class. |
| module | Runs once for each module. |
| package | Runs once for a package. |
| session | Runs once for the complete test session. |
20. Function Scope Fixture
@pytest.fixture(scope="function")
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()
A function-scoped fixture is created separately for each test function that requests it.
21. Module Scope Fixture
@pytest.fixture(scope="module")
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()
A module-scoped fixture can be shared by tests within the same Python module.
22. Session Scope Fixture
@pytest.fixture(scope="session")
def environment():
print("Initialize test environment")
yield
print("Cleanup test environment")
A session-scoped fixture can be useful for resources that should be initialized once for the entire test session.
23. PyTest Parameterization
Parameterization allows the same test to execute with different input values.
import pytest
@pytest.mark.parametrize(
"a,b,expected",
[
(10, 20, 30),
(5, 5, 10),
(100, 50, 150)
]
)
def test_addition(a, b, expected):
assert a + b == expected
The same test executes once for every data set.
24. Parameterization Flow
Test Function
|
v
@pytest.mark.parametrize
|
+---- Data Set 1
|
+---- Data Set 2
|
+---- Data Set 3
|
v
Multiple Test Executions
25. Parameterized Selenium Test
Parameterization is very useful in Selenium automation.
import pytest
from selenium import webdriver
@pytest.mark.parametrize(
"url",
[
"https://www.google.com",
"https://www.example.com"
]
)
def test_title(url):
driver = webdriver.Chrome()
driver.get(url)
assert driver.title != ""
driver.quit()
26. PyTest Markers
Markers are used to categorize and selectively execute tests.
import pytest
@pytest.mark.smoke
def test_login():
assert True
@pytest.mark.regression
def test_search():
assert True
Markers make it easier to organize large test suites.
27. Registering Custom Markers
Custom markers can be registered in the pytest.ini file.
[pytest]
markers =
smoke: smoke test cases
regression: regression test cases
login: login related tests
This provides a clear definition for custom markers used in the project.
28. Running Tests by Marker
To run only smoke tests:
pytest -m smoke
To run regression tests:
pytest -m regression
To run login tests:
pytest -m login
29. Skipping Tests
PyTest allows tests to be skipped when they should not execute under a particular condition.
import pytest
@pytest.mark.skip(reason="Feature is not ready")
def test_new_feature():
assert True
The test appears as skipped instead of failed.
30. Conditional Skipping
A test can be skipped based on a condition.
import pytest
import sys
@pytest.mark.skipif(
sys.platform == "win32",
reason="Not supported on Windows"
)
def test_linux_feature():
assert True
This is useful when tests depend on a specific operating system, Python version, browser, or environment.
31. Expected Failures
The xfail marker can be used for tests that are currently expected to fail.
import pytest
@pytest.mark.xfail(reason="Known application defect")
def test_known_issue():
assert 1 == 2
This allows known issues to be documented without treating them as unexpected failures.
32. PyTest Configuration File
PyTest can be configured using files such as pytest.ini.
[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts = -v
Configuration files help standardize test execution across the project.
33. PyTest Project Structure
A basic PyTest automation project can be organized as follows:
project/
|-- tests/
| |-- test_login.py
| |-- test_search.py
| |-- test_checkout.py
|
|-- pages/
| |-- login_page.py
| |-- search_page.py
| |-- checkout_page.py
|
|-- utilities/
| |-- driver_factory.py
| |-- config_reader.py
|
|-- conftest.py
|-- pytest.ini
|-- requirements.txt
34. Understanding conftest.py
conftest.py is a special PyTest file commonly used to define shared fixtures and test configuration.
Fixtures defined in an appropriate conftest.py can be automatically discovered and used by test files without importing them manually.
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
This fixture can then be used by tests in the relevant test directory.
35. Selenium Fixture Using conftest.py
A common Selenium project design is to place browser setup inside conftest.py.
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
driver.quit()
Test file:
def test_google(driver):
driver.get("https://www.google.com")
assert "Google" in driver.title
36. PyTest with Selenium WebDriver
PyTest and Selenium WebDriver can be combined to build browser automation frameworks.
from selenium import webdriver
def test_google_search():
driver = webdriver.Chrome()
driver.get("https://www.google.com")
assert "Google" in driver.title
driver.quit()
In production frameworks, browser creation and cleanup are usually moved into fixtures.
37. Selenium Login Test with PyTest
def test_login(driver):
driver.get("https://example.com/login")
driver.find_element("id", "username").send_keys("admin")
driver.find_element("id", "password").send_keys("admin123")
driver.find_element("id", "loginButton").click()
assert "Dashboard" in driver.title
This example demonstrates how a PyTest test can control a Selenium browser and validate the result.
38. PyTest with Page Object Model
The Page Object Model (POM) separates web-page interaction logic from test logic.
Example page object:
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username = (By.ID, "username")
self.password = (By.ID, "password")
self.login_button = (By.ID, "loginButton")
def login(self, username, password):
self.driver.find_element(
*self.username
).send_keys(username)
self.driver.find_element(
*self.password
).send_keys(password)
self.driver.find_element(
*self.login_button
).click()
39. PyTest POM Test Example
def test_login(driver):
driver.get("https://example.com/login")
login_page = LoginPage(driver)
login_page.login(
"admin",
"admin123"
)
assert "Dashboard" in driver.title
This approach keeps test logic cleaner and makes page interactions reusable.
40. PyTest Test Dependencies
PyTest itself has a relatively simple dependency model, but Selenium automation projects normally require additional packages.
Example requirements.txt:
pytest
selenium
Additional packages can be added based on project requirements.
41. Installing Selenium with PyTest
pip install selenium pytest
Verify PyTest:
pytest --version
Verify Selenium:
pip show selenium
42. PyTest Verbose Mode
Verbose mode provides more information about each test during execution.
pytest -v
Example conceptual output:
test_login.py::test_valid_login PASSED
test_login.py::test_invalid_login PASSED
test_search.py::test_search PASSED
43. Running Specific Test Files
A specific test file can be executed directly.
pytest tests/test_login.py
Multiple files can also be supplied:
pytest tests/test_login.py tests/test_search.py
44. Running a Specific Test Function
pytest tests/test_login.py::test_valid_login
This is useful when debugging a specific test instead of executing the complete test suite.
45. Running Tests by Keyword
The -k option can be used to select tests based on their names.
pytest -k login
This can execute tests whose names match the keyword.
46. Stopping After the First Failure
The -x option stops execution after the first failed test.
pytest -x
This can be useful while debugging a failing test suite.
47. Running a Limited Number of Failures
PyTest also supports stopping after a specified number of failures.
pytest --maxfail=2
This stops execution after two test failures.
48. PyTest Output and Test Status
PyTest commonly reports statuses such as:
| Status | Meaning |
| PASSED | Test executed successfully. |
| FAILED | Assertion or test execution failed. |
| SKIPPED | Test was intentionally skipped. |
| XFAIL | Test was expected to fail. |
| XPASS | Expected-failure test unexpectedly passed. |
| ERROR | Setup, fixture, collection, or another execution error occurred. |
49. PyTest Test Reports
PyTest can generate different types of reports using built-in options and plugins.
For example, a basic report can be generated in JUnit XML format:
pytest --junitxml=report.xml
HTML reports can also be generated using the appropriate PyTest reporting plugin.
pip install pytest-html
Then:
pytest --html=report.html
Reports are useful for reviewing test results and integrating automated testing into CI/CD pipelines.
50. PyTest HTML Reports
The pytest-html plugin can generate an HTML report containing test execution information.
pip install pytest-html
Run tests with:
pytest --html=report.html
The generated HTML report can contain information about passed, failed, skipped, and other test outcomes.
51. PyTest JUnit XML Reports
JUnit XML reports are commonly used by CI/CD and test management systems.
pytest --junitxml=results.xml
These reports can be consumed by various continuous integration systems and reporting tools.
52. PyTest Logging
Logging can help identify what happened during test execution.
import logging
def test_login():
logging.info("Starting login test")
result = True
assert result is True
For larger frameworks, logging configuration can be centralized so that test execution information is stored consistently.
53. PyTest Setup and Teardown
Setup logic prepares the environment before a test, while teardown logic cleans resources after execution.
Fixtures are the preferred mechanism for many setup and teardown requirements in PyTest.
@pytest.fixture
def setup():
print("Setup")
yield
print("Teardown")
54. Autouse Fixtures
An autouse fixture executes automatically without needing to explicitly request the fixture in every test.
@pytest.fixture(autouse=True)
def setup():
print("Setup before test")
yield
print("Cleanup after test")
Autouse fixtures should be used carefully because they can affect many tests implicitly.
55. Fixture Dependency
One fixture can use another fixture as a dependency.
@pytest.fixture
def username():
return "admin"
@pytest.fixture
def login_data(username):
return {
"username": username,
"password": "admin123"
}
def test_login(login_data):
assert login_data["username"] == "admin"
This makes complex test setup easier to organize.
56. PyTest Parameterization with Multiple Values
import pytest
@pytest.mark.parametrize(
"username,password",
[
("admin", "admin123"),
("manager", "manager123"),
("employee", "employee123")
]
)
def test_login(username, password):
assert username != ""
assert password != ""
The same test executes for every username and password combination.
57. Parameterization with Expected Results
import pytest
@pytest.mark.parametrize(
"username,password,expected",
[
("admin", "admin123", True),
("invalid", "wrong", False),
("manager", "manager123", True)
]
)
def test_login_validation(username, password, expected):
result = username != "invalid"
assert result == expected
Expected results can be stored along with the test inputs.
58. Parameterized Selenium Search Test
import pytest
@pytest.mark.parametrize(
"search_text",
[
"Laptop",
"Mobile",
"Headphones",
"Keyboard"
]
)
def test_search(driver, search_text):
driver.get("https://example.com")
search_box = driver.find_element(
"id",
"search"
)
search_box.send_keys(search_text)
assert search_text != ""
59. PyTest Fixtures and Parameterization Together
Fixtures and parameterization can be combined to build reusable Selenium tests.
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
@pytest.mark.parametrize(
"search_text",
["Laptop", "Mobile", "Tablet"]
)
def test_search(driver, search_text):
driver.get("https://example.com")
driver.find_element(
"id",
"search"
).send_keys(search_text)
60. PyTest with Multiple Browsers
Browser parameterization can be used to run similar Selenium tests across different browsers.
import pytest
from selenium import webdriver
@pytest.fixture
def driver(request):
browser = request.param
if browser == "chrome":
driver = webdriver.Chrome()
elif browser == "firefox":
driver = webdriver.Firefox()
else:
raise ValueError(
f"Unsupported browser: {browser}"
)
yield driver
driver.quit()
@pytest.mark.parametrize(
"driver",
["chrome", "firefox"],
indirect=True
)
def test_browser(driver):
driver.get("https://www.google.com")
assert driver.title != ""
61. PyTest request Fixture
The built-in request fixture provides information about the current test context and can be useful for advanced fixture designs.
@pytest.fixture
def browser(request):
browser_name = request.param
print("Browser:", browser_name)
This is especially useful when creating configurable browser fixtures.
62. PyTest Skip Based on Browser
Tests can be conditionally skipped depending on the browser or environment.
@pytest.mark.skip(reason="Feature unavailable in this browser")
def test_browser_specific_feature():
assert True
Conditional logic can also be implemented through fixtures or configuration when browser-specific behavior is required.
63. PyTest Plugins
PyTest has a large ecosystem of plugins that extend its functionality.
Common examples include:
- pytest-html: HTML test reports.
- pytest-xdist: Parallel test execution.
- pytest-cov: Code coverage reporting.
- pytest-mock: Mocking support.
- pytest-json-report: JSON-based reporting.
Plugins allow teams to customize PyTest for different automation requirements.
64. PyTest Parallel Execution
Parallel execution can reduce overall test execution time. The pytest-xdist plugin is commonly used for parallel execution.
pip install pytest-xdist
Run tests using multiple workers:
pytest -n 4
This requests four workers for parallel test execution.
When using Selenium, each parallel test should have an isolated browser session and thread-safe framework resources.
65. PyTest and Selenium Thread Safety
When Selenium tests run in parallel, WebDriver instances should generally not be shared between independent concurrent tests.
Test 1 ----> WebDriver 1 ----> Browser 1
Test 2 ----> WebDriver 2 ----> Browser 2
Test 3 ----> WebDriver 3 ----> Browser 3
Using separate browser sessions helps prevent one test from interfering with another.
66. PyTest and API Testing
Although PyTest is commonly associated with unit and Selenium testing, it can also be used for API automation.
import requests
def test_get_users():
response = requests.get(
"https://api.example.com/users"
)
assert response.status_code == 200
The same PyTest concepts such as fixtures, parameterization, markers, and assertions can be applied to API tests.
67. PyTest for Data-Driven API Testing
import pytest
import requests
@pytest.mark.parametrize(
"user_id",
[1, 2, 3]
)
def test_user_api(user_id):
response = requests.get(
f"https://api.example.com/users/{user_id}"
)
assert response.status_code == 200
This demonstrates how one test can validate multiple API inputs.
68. PyTest Environment Configuration
Test frameworks often need different environments such as QA, staging, and production-like environments.
def test_environment():
environment = "QA"
base_url = "https://qa.example.com"
assert environment == "QA"
assert base_url.startswith("https://")
In production frameworks, environment values are generally maintained through configuration rather than hard-coded throughout test files.
69. PyTest Configuration Using Environment Variables
Environment variables can be used to provide configuration values without modifying test code.
import os
def test_environment():
base_url = os.getenv(
"BASE_URL",
"https://example.com"
)
assert base_url.startswith("https://")
This approach is useful in CI/CD environments where configuration changes between environments.
70. PyTest with Page Objects and Fixtures
A scalable Selenium framework can combine fixtures, Page Object Model, parameterization, and assertions.
@pytest.fixture
def login_page(driver):
return LoginPage(driver)
def test_login(login_page):
login_page.login(
"admin",
"admin123"
)
assert "Dashboard" in login_page.driver.title
This structure keeps browser management, page interactions, and test validation separated.
71. PyTest Test Organization
Large automation projects should organize tests logically.
tests/
|-- smoke/
| |-- test_login.py
| |-- test_home.py
|
|-- regression/
| |-- test_search.py
| |-- test_checkout.py
|
|-- api/
| |-- test_users.py
| |-- test_products.py
Markers and directory structure can be combined to improve test suite organization.
72. PyTest Smoke Testing
Smoke tests validate critical application functionality quickly after a new build or deployment.
import pytest
@pytest.mark.smoke
def test_login():
assert True
@pytest.mark.smoke
def test_home_page():
assert True
Smoke tests can be executed using:
pytest -m smoke
73. PyTest Regression Testing
Regression tests validate existing functionality after application changes.
import pytest
@pytest.mark.regression
def test_search():
assert True
@pytest.mark.regression
def test_checkout():
assert True
Regression tests can be executed using:
pytest -m regression
74. PyTest Common Mistakes
- Incorrect test file naming.
- Incorrect test function naming.
- Forgetting to install PyTest.
- Using incorrect fixture names.
- Not closing Selenium browsers.
- Sharing WebDriver instances unsafely between parallel tests.
- Using excessive hard-coded test data.
- Not using assertions properly.
- Creating overly complicated fixtures.
- Using too many implicit dependencies between tests.
- Not organizing tests into logical modules.
- Not configuring markers correctly.
- Ignoring test failures in CI/CD.
- Storing sensitive credentials directly in source code.
75. Common Selenium + PyTest Mistakes
| Mistake | Better Approach |
| Creating browser in every test manually | Use reusable fixtures. |
| Forgetting driver.quit() | Use fixture teardown. |
| Sharing one driver in parallel tests | Use isolated WebDriver instances. |
| Hard-coding URLs everywhere | Use configuration. |
| Putting locators directly in every test | Use Page Object Model. |
| Duplicating test data | Use parameterization or external data. |
76. Best Practices for PyTest
- Follow standard PyTest naming conventions.
- Keep test cases small and focused.
- Use meaningful test names.
- Use fixtures for reusable setup and cleanup.
- Use parameterization for data-driven tests.
- Use markers to categorize tests.
- Keep Selenium page interactions inside Page Objects.
- Keep configuration separate from test logic.
- Generate useful test reports.
- Use logging for debugging.
- Avoid unnecessary test dependencies.
- Use isolated browser sessions for parallel execution.
- Protect passwords, tokens, and other secrets.
- Integrate automated tests into CI/CD pipelines.
77. PyTest vs unittest
| Feature | PyTest | unittest |
| Syntax | Simple | More structured |
| Assertions | Python assert | unittest assertion methods |
| Fixtures | Powerful fixture system | setUp/tearDown mechanisms |
| Parameterization | Built-in through markers | Requires additional approaches |
| Plugins | Large ecosystem | Smaller ecosystem |
| Selenium Integration | Commonly used | Supported |
78. PyTest vs TestNG
| Feature | PyTest | TestNG |
| Language | Python | Java |
| Basic Assertion | assert | Assert methods |
| Fixtures/Setup | pytest.fixture | Configuration annotations |
| Parameterization | parametrize | DataProvider / Parameters |
| Markers | Yes | Groups and other annotations |
| Web Automation | Selenium Python | Selenium Java |
| Reports | Plugins and formats | Built-in/plugin-based reporting options |
79. PyTest with CI/CD
PyTest can be integrated into continuous integration and continuous delivery pipelines.
Developer Commit
|
v
CI/CD Pipeline
|
v
Install Dependencies
|
v
Run PyTest
|
v
Selenium / API Tests
|
v
Generate Reports
|
v
Build Result
This allows automated tests to execute whenever new application code is committed or deployed.
80. PyTest with Jenkins
Jenkins can execute PyTest commands as part of an automated pipeline.
pytest -v
pytest --html=report.html
pytest --junitxml=results.xml
Test reports can then be archived or processed by the CI system according to the project's reporting configuration.
81. PyTest Automation Framework Architecture
Test Data
|
v
Parameterization
|
v
PyTest Test Case
|
+----------+----------+
| |
v v
Fixtures Page Objects
| |
+----------+----------+
|
v
Selenium WebDriver
|
v
Web Browser
|
v
Application
|
v
Assertions
|
v
Reports
82. Complete Practical Selenium PyTest Example
The following example demonstrates a simple reusable Selenium PyTest framework using a fixture, Selenium WebDriver, and assertions.
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
driver.quit()
def test_google_title(driver):
driver.get("https://www.google.com")
assert "Google" in driver.title
def test_google_url(driver):
driver.get("https://www.google.com")
assert "google" in driver.current_url.lower()
83. Practical Login Automation Example
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
driver.quit()
@pytest.mark.parametrize(
"username,password",
[
("admin", "admin123"),
("manager", "manager123"),
("employee", "employee123")
]
)
def test_login(driver, username, password):
driver.get("https://example.com/login")
driver.find_element(
"id",
"username"
).send_keys(username)
driver.find_element(
"id",
"password"
).send_keys(password)
driver.find_element(
"id",
"loginButton"
).click()
assert "Dashboard" in driver.title
This example demonstrates how fixtures and parameterization can reduce duplicate Selenium test code.
84. Practical Search Testing Example
import pytest
@pytest.mark.parametrize(
"keyword",
[
"Laptop",
"Mobile",
"Tablet",
"Headphones",
"Keyboard"
]
)
def test_product_search(driver, keyword):
driver.get("https://example.com")
search = driver.find_element(
"id",
"search"
)
search.clear()
search.send_keys(keyword)
driver.find_element(
"id",
"searchButton"
).click()
assert keyword != ""
85. Practical Project Structure
selenium_pytest_project/
|-- tests/
| |-- test_login.py
| |-- test_search.py
| |-- test_checkout.py
|
|-- pages/
| |-- login_page.py
| |-- search_page.py
| |-- checkout_page.py
|
|-- utilities/
| |-- config_reader.py
| |-- driver_factory.py
|
|-- test_data/
| |-- login_data.json
| |-- search_data.json
|
|-- reports/
| |-- report.html
|
|-- conftest.py
|-- pytest.ini
|-- requirements.txt
86. Recommended PyTest Framework Flow
Test Request
|
v
Test Data
|
v
PyTest Parameterization
|
v
Fixture
|
v
WebDriver
|
v
Page Object
|
v
Application
|
v
Assertion
|
v
Test Result
|
v
Report
87. Interview Questions on PyTest Basics
1. What is PyTest?
PyTest is a Python testing framework used to write and execute automated tests.
2. How do you install PyTest?
PyTest can be installed using pip install pytest.
3. How do you run PyTest?
The basic command is pytest.
4. What is a PyTest fixture?
A fixture provides reusable setup, test resources, data, and cleanup functionality.
5. What is conftest.py?
conftest.py is a special PyTest configuration file commonly used to define reusable fixtures and shared test configuration.
6. What is @pytest.mark.parametrize?
It allows the same test to execute with multiple sets of input data.
7. What are PyTest markers?
Markers categorize tests and allow specific groups of tests to be executed selectively.
8. How do you skip a test?
The @pytest.mark.skip marker can be used to skip a test.
9. What is xfail?
xfail marks a test as expected to fail because of a known or expected condition.
10. How do you run a specific test?
You can use a command such as pytest test_login.py::test_login.
11. How do you run tests in verbose mode?
Use pytest -v.
12. How can PyTest be used with Selenium?
Selenium WebDriver can be initialized through PyTest fixtures and used inside test functions to automate browsers.
13. Why are fixtures useful in Selenium automation?
Fixtures provide reusable browser setup and cleanup logic and help prevent duplicate WebDriver code.
14. What is parameterization?
Parameterization allows the same test logic to execute using different input values.
15. How can PyTest generate HTML reports?
The pytest-html plugin can be installed and used to generate HTML reports.
16. How can PyTest generate JUnit reports?
PyTest supports JUnit XML output using the --junitxml option.
17. What is pytest.ini?
pytest.ini is a configuration file that can define test paths, naming patterns, markers, and default options.
18. How can PyTest tests run in parallel?
The pytest-xdist plugin can be used for parallel execution.
19. What is the difference between fixture scope and test parameterization?
Fixture scope controls how often a fixture resource is created, while parameterization controls how many different data combinations a test receives.
20. What are important PyTest best practices?
Use reusable fixtures, clear test names, parameterization, Page Object Model for Selenium, meaningful assertions, organized test suites, secure configuration, and useful reporting.
88. Quick Reference Table
| Concept | Purpose |
| pytest | Runs the PyTest test suite. |
| assert | Validates expected conditions. |
| @pytest.fixture | Creates reusable setup and cleanup logic. |
| conftest.py | Stores shared fixtures and configuration. |
| @pytest.mark.parametrize | Runs tests with multiple data sets. |
| @pytest.mark.smoke | Categorizes smoke tests. |
| @pytest.mark.regression | Categorizes regression tests. |
| @pytest.mark.skip | Skips a test. |
| @pytest.mark.xfail | Marks an expected failure. |
| pytest -v | Runs tests in verbose mode. |
| pytest -k | Selects tests using keywords. |
| pytest -x | Stops after the first failure. |
| --maxfail | Limits the number of failures before stopping. |
| --html | Generates an HTML report with pytest-html. |
| --junitxml | Generates JUnit XML output. |
| pytest-xdist | Provides parallel test execution. |
89. Learning Roadmap for PyTest
- Learn Python fundamentals.
- Understand basic software testing concepts.
- Install and configure PyTest.
- Create basic test functions.
- Learn assertions.
- Understand test discovery.
- Learn test classes.
- Learn PyTest fixtures.
- Understand fixture scopes.
- Learn conftest.py.
- Learn parameterization.
- Learn markers.
- Learn skip and xfail.
- Learn PyTest configuration.
- Integrate PyTest with Selenium.
- Learn Page Object Model.
- Build reusable Selenium fixtures.
- Learn reporting.
- Learn parallel execution.
- Integrate PyTest with CI/CD.
- Build a complete Selenium PyTest framework.
90. Practical Exercises
- Create a simple addition test using PyTest.
- Create a calculator test with multiple assertions.
- Create a login test using a Selenium fixture.
- Create a parameterized login test.
- Create a parameterized search test.
- Create smoke and regression markers.
- Create a reusable browser fixture in conftest.py.
- Create a Page Object Model framework.
- Generate an HTML test report.
- Generate a JUnit XML report.
- Run selected tests using markers.
- Execute tests in parallel using pytest-xdist.
- Integrate the test suite with a CI/CD pipeline.
91. Real-World Selenium PyTest Architecture
Configuration
|
v
conftest.py
|
+-----------+-----------+
| |
v v
Fixtures Test Data
| |
+-----------+-----------+
|
v
PyTest Tests
|
v
Page Object Model
|
v
Selenium WebDriver
|
v
Browser
|
v
Web Application
|
v
Assertions
|
v
Reports
|
v
CI/CD
92. Summary
PyTest is a powerful and flexible Python testing framework used for creating automated tests. Its simple syntax makes it easy to write basic tests, while features such as fixtures, parameterization, markers, configuration, plugins, and reporting support large automation frameworks.
For Selenium automation, PyTest can manage browser setup and cleanup through fixtures, execute tests with multiple data sets using parameterization, organize tests using markers, and separate application interaction logic through the Page Object Model.
PyTest can also be integrated with HTML and JUnit reporting, parallel execution, API testing, CI/CD systems, and other automation tools.
The combination of PyTest + Selenium + Fixtures + Parameterization + Page Object Model + Reporting provides a strong foundation for building maintainable Python-based web automation frameworks.
93. Course Resources
Learn more about Selenium automation and related testing concepts:
Final Takeaway: PyTest provides a simple yet powerful foundation for Python automation testing. When combined with Selenium WebDriver, fixtures, parameterization, Page Object Model, markers, reporting, and CI/CD, it can be used to build structured, reusable, scalable, and maintainable web automation frameworks.